1   /*
2    * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4    *
5    * This code is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU General Public License version 2 only, as
7    * published by the Free Software Foundation.  Oracle designates this
8    * particular file as subject to the "Classpath" exception as provided
9    * by Oracle in the LICENSE file that accompanied this code.
10   *
11   * This code is distributed in the hope that it will be useful, but WITHOUT
12   * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14   * version 2 for more details (a copy is included in the LICENSE file that
15   * accompanied this code).
16   *
17   * You should have received a copy of the GNU General Public License version
18   * 2 along with this work; if not, write to the Free Software Foundation,
19   * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20   *
21   * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22   * or visit www.oracle.com if you need additional information or have any
23   * questions.
24   */
25  
26  package sun.io;
27  
28  import java.io.UnsupportedEncodingException;
29  import java.lang.ref.SoftReference;
30  import java.util.Properties;
31  
32  /**
33   * Package-private utility class that caches the default converter classes and
34   * provides other logic common to both the ByteToCharConverter and
35   * CharToByteConverter classes.
36   *
37   * @author   Mark Reinhold
38   * @since    1.2
39   *
40   * @deprecated Replaced by {@link java.nio.charset}.  THIS API WILL BE
41   * REMOVED IN J2SE 1.6.
42   */
43  @Deprecated
44  public class Converters {
45  
46      private Converters() { }    /* To prevent instantiation */
47  
48      /* Lock for all static fields in this class */
49      private static Object lock = Converters.class;
50  
51      /* Cached values of system properties */
52      private static String converterPackageName = null;  /* file.encoding.pkg */
53      private static String defaultEncoding = null;       /* file.encoding */
54  
55      /* Converter type constants and names */
56      public static final int BYTE_TO_CHAR = 0;
57      public static final int CHAR_TO_BYTE = 1;
58      private static final String[] converterPrefix = { "ByteToChar",
59                                                        "CharToByte" };
60  
61  
62      // -- Converter class cache --
63  
64      private static final int CACHE_SIZE = 3;
65  
66      /* For the default charset, whatever it turns out to be */
67      private static final Object DEFAULT_NAME = new Object();
68  
69      /* Cached converter classes, CACHE_SIZE per converter type.  Each cache
70       * entry is a soft reference to a two-object array; the first element of
71       * the array is the converter class, the second is an object (typically a
72       * string) representing the encoding name that was used to request the
73       * converter, e.g.,
74       *
75       *     ((Object[])classCache[CHAR_TO_BYTE][i].get())[0]
76       *
77       * will be a CharToByteConverter and
78       *
79       *     ((Object[])classCache[CHAR_TO_BYTE][i].get())[1]
80       *
81       * will be the string encoding name used to request it, assuming that cache
82       * entry i is valid.
83       *
84       * Ordinarily we'd do this with a private static utility class, but since
85       * this code can be involved in the startup sequence it's important to keep
86       * the footprint down.
87       */
88      @SuppressWarnings("unchecked")
89      private static SoftReference<Object[]>[][] classCache
90          = (SoftReference<Object[]>[][]) new SoftReference<?>[][] {
91              new SoftReference<?>[CACHE_SIZE],
92              new SoftReference<?>[CACHE_SIZE]
93          };
94  
95      private static void moveToFront(Object[] oa, int i) {
96          Object ob = oa[i];
97          for (int j = i; j > 0; j--)
98              oa[j] = oa[j - 1];
99          oa[0] = ob;
100     }
101 
102     private static Class<?> cache(int type, Object encoding) {
103         SoftReference<Object[]>[] srs = classCache[type];
104         for (int i = 0; i < CACHE_SIZE; i++) {
105             SoftReference<Object[]> sr = srs[i];
106             if (sr == null)
107                 continue;
108             Object[] oa = sr.get();
109             if (oa == null) {
110                 srs[i] = null;
111                 continue;
112             }
113             if (oa[1].equals(encoding)) {
114                 moveToFront(srs, i);
115                 return (Class<?>)oa[0];
116             }
117         }
118         return null;
119     }
120 
121     private static Class<?> cache(int type, Object encoding, Class<?> c) {
122         SoftReference<Object[]>[] srs = classCache[type];
123         srs[CACHE_SIZE - 1] = new SoftReference<>(new Object[] { c, encoding });
124         moveToFront(srs, CACHE_SIZE - 1);
125         return c;
126     }
127 
128     /* Used to avoid doing expensive charset lookups for charsets that are not
129      * yet directly supported by NIO.
130      */
131     public static boolean isCached(int type, String encoding) {
132         synchronized (lock) {
133             SoftReference<Object[]>[] srs = classCache[type];
134             for (int i = 0; i < CACHE_SIZE; i++) {
135                 SoftReference<Object[]> sr = srs[i];
136                 if (sr == null)
137                     continue;
138                 Object[] oa = sr.get();
139                 if (oa == null) {
140                     srs[i] = null;
141                     continue;
142                 }
143                 if (oa[1].equals(encoding))
144                     return true;
145             }
146             return false;
147         }
148     }
149 
150 
151 
152     /** Get the name of the converter package */
153     private static String getConverterPackageName() {
154         String cp = converterPackageName;
155         if (cp != null) return cp;
156         java.security.PrivilegedAction<String> pa =
157             new sun.security.action.GetPropertyAction("file.encoding.pkg");
158         cp = java.security.AccessController.doPrivileged(pa);
159         if (cp != null) {
160             /* Property is set, so take it as the true converter package */
161             converterPackageName = cp;
162         } else {
163             /* Fall back to sun.io */
164             cp = "sun.io";
165         }
166         return cp;
167     }
168 
169     public static String getDefaultEncodingName() {
170         synchronized (lock) {
171             if (defaultEncoding == null) {
172                 java.security.PrivilegedAction<String> pa =
173                     new sun.security.action.GetPropertyAction("file.encoding");
174                 defaultEncoding = java.security.AccessController.doPrivileged(pa);
175             }
176         }
177         return defaultEncoding;
178     }
179 
180     public static void resetDefaultEncodingName() {
181         // This method should only be called during VM initialization.
182         if (sun.misc.VM.isBooted())
183             return;
184 
185         synchronized (lock) {
186             defaultEncoding = "ISO-8859-1";
187             Properties p = System.getProperties();
188             p.setProperty("file.encoding", defaultEncoding);
189             System.setProperties(p);
190         }
191     }
192 
193     /**
194      * Get the class that implements the given type of converter for the named
195      * encoding, or throw an UnsupportedEncodingException if no such class can
196      * be found
197      */
198     private static Class<?> getConverterClass(int type, String encoding)
199         throws UnsupportedEncodingException
200     {
201         String enc = null;
202 
203         /* "ISO8859_1" is the canonical name for the ISO-Latin-1 encoding.
204            Native code in the JDK commonly uses the alias "8859_1" instead of
205            "ISO8859_1".  We hardwire this alias here in order to avoid loading
206            the full alias table just for this case. */
207         if (!encoding.equals("ISO8859_1")) {
208             if (encoding.equals("8859_1")) {
209                 enc = "ISO8859_1";
210             /*
211              * On Solaris with nl_langinfo() called in GetJavaProperties():
212              *
213              *   locale undefined -> NULL -> hardcoded default
214              *   "C" locale       -> "" -> hardcoded default    (on 2.6)
215              *   "C" locale       -> "646"                      (on 2.7)
216              *   "en_US" locale -> "ISO8859-1"
217              *   "en_GB" locale -> "ISO8859-1"                  (on 2.7)
218              *   "en_UK" locale -> "ISO8859-1"                  (on 2.6)
219              */
220             } else if (encoding.equals("ISO8859-1")) {
221                 enc = "ISO8859_1";
222             } else if (encoding.equals("646")) {
223                 enc = "ASCII";
224             } else {
225                 enc = CharacterEncoding.aliasName(encoding);
226             }
227         }
228         if (enc == null) {
229             enc = encoding;
230         }
231 
232         try {
233             return Class.forName(getConverterPackageName()
234                                  + "." + converterPrefix[type] + enc);
235         } catch(ClassNotFoundException e) {
236             throw new UnsupportedEncodingException(enc);
237         }
238 
239     }
240 
241     /**
242      * Instantiate the given converter class, or throw an
243      * UnsupportedEncodingException if it cannot be instantiated
244      */
245     private static Object newConverter(String enc, Class<?> c)
246         throws UnsupportedEncodingException
247     {
248         try {
249             return c.newInstance();
250         } catch(InstantiationException e) {
251             throw new UnsupportedEncodingException(enc);
252         } catch(IllegalAccessException e) {
253             throw new UnsupportedEncodingException(enc);
254         }
255     }
256 
257     /**
258      * Create a converter object that implements the given type of converter
259      * for the given encoding, or throw an UnsupportedEncodingException if no
260      * appropriate converter class can be found and instantiated
261      */
262     static Object newConverter(int type, String enc)
263         throws UnsupportedEncodingException
264     {
265         Class<?> c;
266         synchronized (lock) {
267             c = cache(type, enc);
268             if (c == null) {
269                 c = getConverterClass(type, enc);
270                 if (!c.getName().equals("sun.io.CharToByteUTF8"))
271                     cache(type, enc, c);
272             }
273         }
274         return newConverter(enc, c);
275     }
276 
277     /**
278      * Find the class that implements the given type of converter for the
279      * default encoding.  If the default encoding cannot be determined or is
280      * not yet defined, return a class that implements the fallback default
281      * encoding, which is just ISO 8859-1.
282      */
283     private static Class<?> getDefaultConverterClass(int type) {
284         boolean fillCache = false;
285         Class<?> c;
286 
287         /* First check the class cache */
288         c = cache(type, DEFAULT_NAME);
289         if (c != null)
290             return c;
291 
292         /* Determine the encoding name */
293         String enc = getDefaultEncodingName();
294         if (enc != null) {
295             /* file.encoding has been set, so cache the converter class */
296             fillCache = true;
297         } else {
298             /* file.encoding has not been set, so use a default encoding which
299                will not be cached */
300             enc = "ISO8859_1";
301         }
302 
303         /* We have an encoding name; try to find its class */
304         try {
305             c = getConverterClass(type, enc);
306             if (fillCache) {
307                 cache(type, DEFAULT_NAME, c);
308             }
309         } catch (UnsupportedEncodingException x) {
310             /* Can't find the default class, so fall back to ISO 8859-1 */
311             try {
312                 c = getConverterClass(type, "ISO8859_1");
313             } catch (UnsupportedEncodingException y) {
314                 throw new InternalError("Cannot find default "
315                                         + converterPrefix[type]
316                                         + " converter class");
317             }
318         }
319         return c;
320 
321     }
322 
323     /**
324      * Create a converter object that implements the given type of converter
325      * for the default encoding, falling back to ISO 8859-1 if the default
326      * encoding cannot be determined.
327      */
328     static Object newDefaultConverter(int type) {
329         Class<?> c;
330         synchronized (lock) {
331             c = getDefaultConverterClass(type);
332         }
333         try {
334             return newConverter("", c);
335         } catch (UnsupportedEncodingException x) {
336             throw new InternalError("Cannot instantiate default converter"
337                                     + " class " + c.getName());
338         }
339     }
340 
341 }